#pragma once

#include <iostream>
#include <cstdio>
#include <cstdarg>
#include <ctime>
#include <string>
#include <cstring>


#define OPEN_LOG 0          // 打开log

#define LOG_TO_FILE  0      // 输出到文件中
#define LOG_TO_CONSOLE  1   // 输出到命令行

#define LOGFILE "./_server.log" //输入的文件路径

// 日志是有日志级别的
enum LOG_EVEL {
    _DEBUG = 0,
    NORMAL = 1,  // 正常
    WARNING = 2, // 警告
    ERROR = 3,   // 错误
    FATAL = 4   // 致命
};

#define LOG_CYC(level, message, ...) Log(level, __FILE__, __LINE__, message, __VA_ARGS__)

const char* gLevelMap[] = {
    "DEBUG",
    "NORMAL",
    "WARNING",
    "ERROR",
    "FATAL" };

const char* gColorMap[] = {
    "\033[0m",    // 0: 默认颜色
    "\033[1;34m", // 1: DEBUG: 蓝色
    "\033[1;32m", // 2: NORMAL: 绿色
    "\033[1;33m", // 3: WARNING: 黄色
    "\033[1;31m", // 4: ERROR: 红色
    "\033[1;35m", // 5: FATAL: 紫色
    "\033[1;36m", // 6: 自定义颜色1: 青色
    "\033[1;37m", // 7: 自定义颜色2: 白色
    "\033[1;38m", // 8: 自定义颜色3: 亮灰色
    "\033[1;39m", // 9: 自定义颜色4: 默认颜色
    "\033[1;30m", // 10: 黑色
    "\033[1;90m", // 11: 深灰色
    "\033[1;91m", // 12: 深红色
    "\033[1;92m", // 13: 深绿色
    "\033[1;93m", // 14: 深黄色
    "\033[1;94m", // 15: 深蓝色
    "\033[1;95m", // 16: 深紫色
    "\033[1;96m", // 17: 深青色
    "\033[1;97m", // 18: 浅灰色
    "\033[1;98m", // 19: 自定义颜色5: 橙色
    "\033[1;99m"  // 20: 自定义颜色6: 粉色
};


//时间戳转化为时间 毫秒级
std::string Stamp2Time(time_t timestamp) {
    time_t tick = (time_t)timestamp;//转换时间
    struct tm tm;
    char s[40];
    tm = *localtime(&tick);
    strftime(s, sizeof(s), "%Y-%m-%d %H:%M:%S", &tm);
    std::string str(s);

    return str;
}

void Log(int level, std::string file_name, const int line, std::string format, ...) {
#if OPEN_LOG

    va_list args;                     // c可变参数对象
    char stdBuffer[1024];             // 标准部分缓冲区
    char logBuffer[1024];             // 自定义部分缓冲区
    time_t timestamp = time(nullptr); // 获取时间戳

    // 将format后面的可变参数输入到args中
    va_start(args, format);

    // 设置日志级别对应的颜色
    const char* colorCode = "\033[0m"; // 默认颜色为终端默认颜色
    switch (level) {
        case _DEBUG:   colorCode = gColorMap[1]; break; // 蓝色
        case NORMAL:  colorCode = gColorMap[2]; break; // 绿色
        case WARNING: colorCode = gColorMap[3]; break; // 黄色
        case ERROR:   colorCode = gColorMap[4]; break; // 红色
        case FATAL:   colorCode = gColorMap[5]; break; // 紫色
    }

    if (LOG_TO_CONSOLE) {
        //  将错误等级和时间戳，连接并打印到标准部分缓冲区
        snprintf(stdBuffer, sizeof stdBuffer, "%s[%s]%s %s[%s]%s %s[%d]%s %s[%s]%s"
            , colorCode, gLevelMap[level], gColorMap[0]
            , gColorMap[9], file_name.c_str(), gColorMap[0]
            , gColorMap[15], line, gColorMap[0]
            , gColorMap[16], Stamp2Time(timestamp).c_str(), gColorMap[0]);

        // 将format和可变参数，连接并打印到自定义部分缓冲区
        vsnprintf(logBuffer, sizeof logBuffer, format.c_str(), args);
        va_end(args);

        // 输出日志到标准输出
        printf("%s%s%s%s\n", stdBuffer, colorCode, logBuffer, gColorMap[0]);
    }

    if (LOG_TO_FILE) {
        //  将错误等级和时间戳，连接并打印到标准部分缓冲区
        snprintf(stdBuffer, sizeof stdBuffer, "[%s] [%s] [%d] [%s] "
            , gLevelMap[level]
            , file_name.c_str()
            , line
            , Stamp2Time(timestamp).c_str());

        // 将format和可变参数，连接并打印到自定义部分缓冲区
        vsnprintf(logBuffer, sizeof(logBuffer), format.c_str(), args);
        va_end(args);

        FILE* fp = fopen(LOGFILE, "a");
        fprintf(fp, "%s%s\n", stdBuffer, logBuffer);
        fclose(fp);
    }

#endif // DEBUG_LOG
}